package org.xx.armory.config;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.binder.DigesterLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;

/**
 * 使用xml文件定义配置的提供程序。
 */
public final class XmlFileConfigurationProvider
        extends FileConfigurationProvider
        implements ConfigurationProvider {
    private final Logger logger = LoggerFactory.getLogger(XmlFileConfigurationProvider.class);
    private final Digester digester;

    /**
     * 构造使用xml文件定义配置的提供程序。
     *
     * @param url
     *         定义配置内的xml文件的资源地址。
     * @throws NullPointerException
     *         如果参数{@code url}是{@code null}。
     */
    public XmlFileConfigurationProvider(
            URL url
    ) {
        super(url);

        this.digester = createDigesterLoader();
    }

    /**
     * 构造使用xml文件定义配置的提供程序。
     *
     * @param fileName
     *         定义配置的xml文件名。
     * @throws NullPointerException
     *         如果参数{@code file}是{@code null}。
     */
    public XmlFileConfigurationProvider(
            String fileName
    ) {
        super(fileName);

        this.digester = createDigesterLoader();
    }

    /**
     * 创建用于解析XML配置文件的Digester。
     *
     * @return 用于解析XML配置文件的Digester。
     */
    private Digester createDigesterLoader() {
        final ConfigurationRulesModule rulesModule = new ConfigurationRulesModule();

        final DigesterLoader loader = newLoader(rulesModule)
                .setNamespaceAware(true)
                .setXIncludeAware(true)
                .setValidating(false);

        try {
            loader.setSchema(rulesModule.loadSchema());
        } catch (IOException | SAXException ex) {
            logger.warn("cannot load configuration schema", ex);
        }

        return loader.newDigester();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Configuration load(
            InputStream stream
    )
            throws IOException {
        try {
            return this.digester.parse(new BufferedInputStream(stream));
        } catch (SAXException ex) {
            throw new IOException("cannot parse xml", ex);
        }
    }
}
